#!/usr/bin/perl
#copyright Ian Baker 2007

use strict;
use Device::SerialPort;
use Time::HiRes qw (time usleep);

my $if0 = new Device::SerialPort('/dev/ttyS0', 0);

$if0->baudrate(19200);
$if0->parity("odd");
$if0->databits(8);
$if0->stopbits(2);
$if0->handshake('none');

die "$!" unless $if0->write_settings;

my $if1 = new Device::SerialPort('/dev/ttyS1', 0);

$if1->baudrate(19200);
$if1->parity("odd");
$if1->databits(8);
$if1->stopbits(2);
$if1->handshake('none');

die "$!" unless $if1->write_settings;

# sensor reading blocky things.
my @sensors1 = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,16);
my @sensors0a = (17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32);
my @sensors0b = (33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,48);

my %sensor_addresses = (
			1 => 0,
			2 => 1,
			3 => 2,
			4 => 3,
			5 => 4,
			6 => 5,
			7 => 6,
			8 => 7,
			9 => 8,
			10 => 9,
			11 => 10,
			12 => 11,
			13 => 12,
			14 => 13,
			15 => 14,
			16 => 15,

			17 => 0,
			18 => 1,
			19 => 2,
			20 => 3,
			21 => 4,
			22 => 5,
			23 => 6,
			24 => 7,
			25 => 8,
			26 => 9,
			27 => 10,
			28 => 11,
			29 => 12,
			30 => 13,
			31 => 14,
			32 => 15,

			33 => 16,
			34 => 17,
			35 => 18,
			36 => 19,
			37 => 20,
			38 => 21,
			39 => 22,
			40 => 23,
			41 => 24,
			42 => 25,
			43 => 26,
			44 => 27,
			45 => 28,
			46 => 29,
			47 => 30,
			48 => 31,
			);

# calibrate the sensors.
my %baseline;
my %baseline_values;
$| = 1;
print "Calibrating...\n";
calibrate($if0, @sensors0a);
print "one!\n";
calibrate($if0, @sensors0b);
print "two!\n";
calibrate($if1, @sensors1);
print "three!\n";

sub calibrate {
  my ($if, @sensors) = @_;
  foreach(1..10) {
    my @read_sensors = @sensors;
    my @write_sensors = map { $sensor_addresses{$_} } @sensors;
    my $first = 1;

    $if->write(pack('C*', @write_sensors));
    usleep(100000);
    pop @read_sensors;

    while(@read_sensors) {
      my ($count, $raw_read) = $if->read(255);
      foreach my $value (unpack('C*', $raw_read)) {
        if($first) {
          undef $first;
          next;
        }
        my $sensor = shift @read_sensors;
        push @{$baseline_values{$sensor}}, $value;
        print ".";
      }
    }
  }
}

foreach my $sensor (sort { $a <=> $b } keys %baseline_values) {
  my $sum;
  foreach(@{$baseline_values{$sensor}}) {
    $sum += $_;
  }
  
  $baseline{$sensor} = $sum / scalar(@{$baseline_values{$sensor}});
  print "$sensor: $baseline{$sensor}\n";
}

print "\nDone.\n";

my $last_time = time();
my %timed;
my $exit = 0;
my @to_write;
my $last_batch = 'b';
while(!$exit) {
  my @read_sensors0;
  my @read_sensors1 = @sensors1;
  
  # jankity...
  if($last_batch eq 'a') {
    @read_sensors0 = @sensors0b;
    $last_batch = 'b';
  } else {
    @read_sensors0 = @sensors0a;
    $last_batch = 'a';
  }

  # translate sensor ids into sensor addresses.
  my @write_sensors0 = map { $sensor_addresses{$_} } @read_sensors0;
  my @write_sensors1 = map { $sensor_addresses{$_} } @read_sensors1;
    
  if(@to_write) {
    # values to write?  stick them on the end of the sensor write array.
    # one at a time...
    my $write = shift @to_write;
    pop @write_sensors0;
    push @write_sensors0, $write;
  }

  # this will also fire any effect that needs firing...
  $if0->write(pack('C*', @write_sensors0));

  $if1->write(pack('C*', @write_sensors1));

  pop @read_sensors0; #remove that last discarded value.
  pop @read_sensors1;

  my $first0 = 1;
  my $first1 = 1;
  
  while(@read_sensors0 || @read_sensors1) {
    my ($count0, $raw_read0) = $if0->read(255);
    my ($count1, $raw_read1) = $if1->read(255);

    next unless ($count1 || $count0);

    # jankity...
    my $pick = 0;
    foreach my $read ($raw_read0, $raw_read1) {
      foreach my $value (unpack('C*', $read)) {
        my $curr;
        if($pick == 0) {
          if($first0) {
            undef $first0;
            next;
          }

          $curr = shift(@read_sensors0);
        } else {
          if($first1) {
            undef $first1;
            next;
          }

          $curr = shift(@read_sensors1);
        }

        push @{$timed{$curr}}, { val => $value, when => time() };

        # trim @timed
        if(scalar(@{$timed{$curr}}) > 4) {
          shift(@{$timed{$curr}});
        } else {
          next;
        }
      
        # average them all.
        my $sum;
        my $first_time = $timed{$curr}->[0]->{when};
        my $last_time;
        foreach(@{$timed{$curr}}) {
          $sum += $_->{val};
          $last_time = $_->{when};
        }
      
        my $avg = $sum / scalar(@{$timed{$curr}});
#        print "$curr: avg: $avg, count: " . scalar(@{$timed{$curr}}) . "\n" if $curr == 9;
      
        if($avg > $baseline{$curr} + 6 && $avg < 60) {
#        if($avg > $baseline{$curr} + 11.5 ) {
          print "$pick / $curr: avg: $avg, count: " . scalar(@{$timed{$curr}}) . ", time: " . sprintf('%.4f', ($last_time - $first_time)) . ", diff: " . ($avg - $baseline{$curr}) . ", baseline: $baseline{$curr}\n";
          print "values: " . join(',', map {$_->{val}} @{$timed{$curr}}) . "\n";
        }
      }
      $pick = 1;
    }
  }
}

